// -----------------------------------------------------------------------------
//
// Work is licensed by Arthur Fermi (C)2008-2011,
// patches are licensed by Mirella Dallagio (C)2011
// under Creative Commons Attribution-Share Alike 3.0 Unported License
// To receive a copy of this license please visit
// http://creativecommons.org/licenses/by-sa/3.0/
//
// You are free to copy, distribute, transmit and adapt the work
// under the following conditions:
//
// You must attribute the work in the manner specified by the 
// author(s) or licensor (but not in any way that suggests that 
// they endorse you or your use of the work). 
//
// If you alter, transform, or build upon this work in any way,
// you may distribute the resulting work only under the same or 
// similar license to this one. 
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See points (6) and (7) of the license for more details.
//
// -----------------------------------------------------------------------------
// This script requires Phoenix LSL preprocessor
// -----------------------------------------------------------------------------
//
// Changelog:
//
// zSN_SimNet_Land 1.02      - 2011-04-24 initial simnet release
// zSN_SimNet_Land 1.03      - 2011-04-26 fix for unban protocol mismatch
// zSN_SimNet_Land 1.03-md3  - 2011-04-26 fix for no time limited ban (land agent side)
// zAF_SimNet_Land 1.03-md7  - 2011-04-30 moving from vanilla lsl to Phoenix LSL preprocessor
//                                        fix for wrong URL on daily web server refresh
//                                        more robust communication with the web server
//                                        new land agent commands '*Ping', '*Reset'
// zAF_SimNet_Land 1.03-md9  - 2011-05-01 separated macro CFG section from implementation
//                                        better debug logic switch, with debug disabled
//                                        no code will be generated for debug
//                                        new commands 'Unsit', '*Clear'
//                                        removed unnecessary variables
//
// -----------------------------------------------------------------------------

// Enter the URL to your SimNet system, make sure to include the last /
// This will likely be http://<my-domain.com>/simnet/
#define BASE_URL                "http://mywebsite.com/simnet/"

// Enter the diagnostic channel for your debug output, 
// or comment next line to disable debug
//#define DIAGNOSTIC_CHANNEL    123456

// -----------------------------------------------------------------------------
// Do not change anything below here or you will likely break something
// -----------------------------------------------------------------------------
// compile with lsl2
// -----------------------------------------------------------------------------

#define HTTP_REQUEST_TIMEOUT    180     // if http request fails, retry in 3 minutes
#define HTTP_REFRESH_INTERVAL   86400   // refresh the listener channel once per day

// -----------------------------------------------------------------------------

integer agent_listener;     // current listener handle
key     web_reqid;          // last web request handle

// -----------------------------------------------------------------------------

// do NOT emit sensitive data to the debug channel!
#ifdef DIAGNOSTIC_CHANNEL
    #define debugList(m) llRegionSay( DIAGNOSTIC_CHANNEL, llList2CSV(m) ) 
#else
    #define debugList(m)
#endif        

#define _v(x) #x,(x)
#define log0(c)         do{ c();          debugList(([ #c ]));              } while(FALSE)
#define log1(c,a1)      do{ c(a1);        debugList(([ #c, (a1) ]));        } while(FALSE)
#define log2(c,a1,a2)   do{ c((a1),(a2)); debugList(([ #c, (a1), (a2) ]));  } while(FALSE)
#define logL(c,l1)      do{ c(l1);        debugList(( #c + (l1) ));         } while(FALSE)

#ifdef DIAGNOSTIC_CHANNEL
    #define snResetScript()             log0( llResetScript                )    
    #define snTeleportAgentHome(i)      log1( llTeleportAgentHome,(i)      )
    #define snEjectFromLand(i)          log1( llEjectFromLand,(i)          )
    #define snUnSit(i)                  log1( llUnSit,(i)                  )    
    #define snAddToLandBanList(i,d)     log2( llAddToLandBanList,(i),(d)   )
    #define snRemoveFromLandBanList(i)  log1( llRemoveFromLandBanList,(i)  )
    #define snResetLandBanList()        log0( llResetLandBanList           )    
    #define snSetParcelMusicURL(u)      log1( llSetParcelMusicURL,(u)      )
    #define snParcelMediaCommandList(l) logL( llParcelMediaCommandList,(l) )
#else
    #define snResetScript               llResetScript    
    #define snTeleportAgentHome         llTeleportAgentHome
    #define snEjectFromLand             llEjectFromLand
    #define snUnSit                     llUnSit    
    #define snAddToLandBanList          llAddToLandBanList
    #define snRemoveFromLandBanList     llRemoveFromLandBanList
    #define snResetLandBanList          llResetLandBanList    
    #define snSetParcelMusicURL         llSetParcelMusicURL
    #define snParcelMediaCommandList    llParcelMediaCommandList
#endif    

// -----------------------------------------------------------------------------

string n2url( float value, integer digits )
{
    string result = (string)((integer)value);
    integer lenght = llStringLength( result );
    if( lenght < digits ) result = llGetSubString( "0000", lenght, digits - 1 ) + result;
    return "/" + result;
}

string pos_url( vector p )
{
    return n2url( p.x, 3 ) + n2url( p.y, 3 ) + n2url( p.z, 4 );
}

string abspos()
{
    return llGetRegionName() + pos_url( llGetPos() );
}

// -----------------------------------------------------------------------------

doTeleportAgentHome( string av )
{
    if( ZERO_VECTOR != llGetAgentSize( av )) // do only if the target is here
        snTeleportAgentHome(av); // Send home (SLOW, 5 seconds delay)
}   

doAddToLandBanList( string av, float duration)
{
    doTeleportAgentHome(av); // Send AV Home if he's still here
    snRemoveFromLandBanList(av); // Remove the entry to avoid limited time ban to add up
    llSleep(0.5);
    snAddToLandBanList(av, duration); // Ban AV
}

doPing( string av )
{
    llInstantMessage(av, llList2CSV([ abspos(), _v(llGetOwner()), llKey2Name(llGetOwner()), _v(agent_listener), llGetScriptName() ])); 
}

// -----------------------------------------------------------------------------

default
{
    changed(integer what)
    {
        if( what & CHANGED_OWNER ) snResetScript();
    }
    
    on_rez(integer dontcare)
    {
        snResetScript();
    }

    state_entry()
    {
        llSetObjectName( "SimNet Land: " + (string)llGetParcelDetails( llGetPos(),[ PARCEL_DETAILS_NAME ]));
        debugList(([ "state_entry" ])); 

        llSetTimerEvent(0.1); // Go Through timer event to request the data
    }
    
    timer()
    {
        debugList(([ "timer" ])); 
        
        llSetTimerEvent(HTTP_REQUEST_TIMEOUT);
        web_reqid = llHTTPRequest(BASE_URL + "simnet_toolbar.cn", [HTTP_METHOD, "POST"], "");   
    }
   
    http_response( key id, integer status, list metadata, string body )
    {
        debugList(([ "http_response", _v(id), _v(status) ])); 
        if( id != web_reqid ) return;
        if( status != 200   ) return;

        // Set Comm Channel
        integer agent_channel = ((integer)llStringTrim( body, STRING_TRIM) * 7) / 3;            
        if (agent_channel == 0) 
        {
            debugList(([ "http_response", "got request to listen on channel 0, ignoring" ])); 
            return;
        }
        
        llListenRemove( agent_listener );
        agent_listener = llListen( agent_channel, "", "", "" ); 
        
        llSetTimerEvent( HTTP_REFRESH_INTERVAL );
        debugList(([ "operating" ])); 
    }    

    listen( integer channel, string name, key id, string message )
    {
        debugList(([ "listen", _v(channel), _v(name), _v(id), _v(message) ])); 
        
        list fields = llParseString2List( message, ["^"], [] );
        string cmd  = llList2String( fields, 0 );
        string arg1 = llList2String( fields, 1 );
        string arg2 = llList2String( fields, 2 );
        string arg3 = llList2String( fields, 3 );

        // Universal commands         
        if     ( cmd == "*Reset"   ) snResetScript();
        else if( cmd == "*Ping"    ) doPing(arg1);
        
        // Parcel local commands (each parcel agent should execute them)
        else if( cmd == "Music"    ) snSetParcelMusicURL( arg1 );
        else if( cmd == "Video"    ) snParcelMediaCommandList(([ PARCEL_MEDIA_COMMAND_URL, arg1 ]));

        else if( cmd == "*Clear"   ) snResetLandBanList();
        else if( cmd == "Unbanned" ) snRemoveFromLandBanList( arg1 );
        else if( cmd == "Banned"   ) doAddToLandBanList( arg1, (float)arg3 );

        // Parcel owner commands (only one agent per owner should run them on each sim)
        else if( cmd == "Home"     ) doTeleportAgentHome( arg1 );
        else if( cmd == "Ejected"  ) snEjectFromLand( arg1 );
        else if( cmd == "Unsit"    ) snUnSit( arg1 );
    }
}

